home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / OutOfPhase1.01Source / OutOfPhase Folder / Level 0 Macintosh 07Aug94 / SoundOutput.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-01  |  12.9 KB  |  424 lines  |  [TEXT/KAHL]

  1. /* SoundOutput.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Debug.h"
  21. #include "Audit.h"
  22. #include "Definitions.h"
  23.  
  24. #pragma options(pack_enums)
  25. #include <Sound.h>
  26. #include <SANE.h>
  27. #include <Errors.h>
  28. #pragma options(!pack_enums)
  29.  
  30. #include "SoundOutput.h"
  31. #include "Memory.h"
  32.  
  33.  
  34. #define MinimumInitialNumberOfBuffers (3)
  35.  
  36. typedef struct MyStructure
  37.     {
  38.         struct MyStructure*        Next;
  39.         struct MyStructure*        Previous;
  40.         SndCommand                        MySoundCommand;
  41.         SndCommand                        MyCallbackCommand;
  42.         volatile short                InUseFlag; /* interrupt level flag; hence "volatile" */
  43.         ExtSoundHeader                Header;
  44.         char*                                    SampleArea;
  45.     } MyStructure;
  46.  
  47.  
  48. static MyBoolean            SoundSystemInUse = False;
  49.  
  50. static MyStructure*        NextAvailableBuffer;
  51. static MyBoolean            IsCurrentBufferCheckedOut;
  52.  
  53. static long                        MaxFramesPerBuffer;
  54. static int                        BytesPerFrame;
  55. static int                        MaxBuffersToAllocate;
  56. static int                        CurrentBufferCount;
  57. static long                        TheSamplingRate;
  58. static MyBoolean            StereoFlag;
  59. static MyBoolean            SixteenBitFlag;
  60.  
  61. static SndChannel*        MySoundChannel;
  62.  
  63.  
  64. static void                            AllocateANewBuffer(void);
  65. static void                            DisposeBuffers(void);
  66. static pascal void            MyCallBack(SndChannel* Channel, SndCommand* Command);
  67.  
  68.  
  69. /* attempt to obtain a sound channel.  returns True if the sound channel was opened */
  70. /* or False if it couldn't be (if already in use or machine doesn't support sound) */
  71. MyBoolean        OpenSoundChannel(long SamplingRate, SoundOutputStereo WantStereo,
  72.                             SoundOutputNumBits NumBits, long FramesPerBuffer, int MaxNumBuffers,
  73.                             int InitialNumBuffers)
  74.     {
  75.         OSErr                            Error;
  76.         SndCommand                Cmd;
  77.         long                            InitOptions;
  78.  
  79.         /* open the sound channel */
  80.         if (SoundSystemInUse)
  81.             {
  82.                 return False;
  83.             }
  84.         InitOptions = initNoInterp /* | initNoDrop */;
  85.         if (WantStereo == eStereo)
  86.             {
  87.                 InitOptions |= initStereo;
  88.             }
  89.         MySoundChannel = NIL;
  90.         Error = SndNewChannel(&MySoundChannel,sampledSynth,InitOptions,
  91.             (SndCallBackProcPtr)&MyCallBack);
  92.         if (Error == noErr)
  93.             {
  94.                 /* we want maximum volume on the sound channel */
  95.                 Cmd.cmd = ampCmd;
  96.                 Cmd.param1 = 255;
  97.                 Cmd.param2 = 0;
  98.                 Error = SndDoImmediate(MySoundChannel,&Cmd);
  99.                 if (Error == noErr)
  100.                     {
  101.                         /* initialize the variables */
  102.                         TheSamplingRate = SamplingRate;
  103.                         StereoFlag = (WantStereo == eStereo);
  104.                         SixteenBitFlag = (NumBits == e16bit);
  105.                         NextAvailableBuffer = NIL;
  106.                         MaxFramesPerBuffer = FramesPerBuffer;
  107.                         BytesPerFrame = 1;
  108.                         if (StereoFlag)
  109.                             {
  110.                                 BytesPerFrame *= 2; /* double the number of words per sample frame */
  111.                             }
  112.                         if (SixteenBitFlag)
  113.                             {
  114.                                 BytesPerFrame *= (sizeof(short) / sizeof(char)); /* words, not bytes */
  115.                             }
  116.                         MaxBuffersToAllocate = MaxNumBuffers;
  117.                         CurrentBufferCount = 0;
  118.                         IsCurrentBufferCheckedOut = False;
  119.                         /* allocate the buffers that were requested to begin with */
  120.                         if (InitialNumBuffers < MinimumInitialNumberOfBuffers)
  121.                             {
  122.                                 InitialNumBuffers = MinimumInitialNumberOfBuffers;
  123.                             }
  124.                         while (InitialNumBuffers > 0)
  125.                             {
  126.                                 AllocateANewBuffer();
  127.                                 InitialNumBuffers -= 1;
  128.                             }
  129.                         /* notice that we escape here */
  130.                         if (CurrentBufferCount > 1)
  131.                             {
  132.                                 /* we need at least 2 buffers, otherwise the sound will skip so */
  133.                                 /* there'd be no point in doing it with 1 buffer.  Other systems */
  134.                                 /* may not have this restriction (e.g. UNIX) */
  135.                                 SoundSystemInUse = True;
  136.                                 return True;
  137.                             }
  138.                         DisposeBuffers();
  139.                     }
  140.                 SndDisposeChannel(MySoundChannel,True);
  141.             }
  142.         return False;
  143.     }
  144.  
  145.  
  146. /* internal routine to add a buffer to the existing ring of buffers. */
  147. static void                AllocateANewBuffer(void)
  148.     {
  149.         MyStructure*        Buffer;
  150.         long double            Fred;
  151.  
  152.         Buffer = (MyStructure*)AllocPtrCanFail(sizeof(MyStructure),"SoundBufHeader");
  153.         if (Buffer != NIL)
  154.             {
  155.                 Buffer->SampleArea = AllocPtrCanFail(MaxFramesPerBuffer
  156.                     * BytesPerFrame,"SndBuffer");
  157.                 if (Buffer->SampleArea == NIL)
  158.                     {
  159.                         ReleasePtr((char*)Buffer);
  160.                         return;
  161.                     }
  162.                 Buffer->InUseFlag = False;
  163.                 Buffer->Header.samplePtr = Buffer->SampleArea;
  164.                 if (SixteenBitFlag)
  165.                     {
  166.                         Buffer->Header.sampleSize = 16;
  167.                     }
  168.                  else
  169.                     {
  170.                         Buffer->Header.sampleSize = 8;
  171.                     }
  172.                 if (StereoFlag)
  173.                     {
  174.                         Buffer->Header.numChannels = 2;
  175.                     }
  176.                  else
  177.                     {
  178.                         Buffer->Header.numChannels = 1;
  179.                     }
  180.                 Buffer->Header.sampleRate = (unsigned long)TheSamplingRate << 16;
  181.                 Buffer->Header.loopStart = 0;
  182.                 Buffer->Header.loopEnd = 0;
  183.                 Buffer->Header.encode = extSH;
  184.                 Buffer->Header.baseFrequency = 64;
  185.                 Buffer->Header.markerChunk = NULL;
  186.                 Buffer->Header.futureUse1 = 0;
  187.                 Buffer->Header.futureUse2 = 0;
  188.                 Buffer->Header.futureUse3 = 0;
  189.                 Buffer->Header.futureUse4 = 0;
  190.                 Fred = TheSamplingRate;
  191.                 x96tox80(&Fred,&(Buffer->Header.AIFFSampleRate)); /* extended type */
  192.                 /* now, link the buffer */
  193.                 if (NextAvailableBuffer != NIL)
  194.                     {
  195.                         /* link this block in */
  196.                         Buffer->Next = NextAvailableBuffer;
  197.                         Buffer->Previous = NextAvailableBuffer->Previous;
  198.                         /* link enclosing buffers to it */
  199.                         NextAvailableBuffer->Previous->Next = Buffer;
  200.                         NextAvailableBuffer->Previous = Buffer;
  201.                         /* stick it where we can use it */
  202.                         NextAvailableBuffer = Buffer;
  203.                     }
  204.                  else
  205.                     {
  206.                         Buffer->Next = Buffer;
  207.                         Buffer->Previous = Buffer;
  208.                         NextAvailableBuffer = Buffer;
  209.                     }
  210.                 CurrentBufferCount += 1;
  211.             }
  212.     }
  213.  
  214.  
  215. /* internal routine to dispose of all of the buffers */
  216. /* don't call this unless they're InUseFlag is clear!!! */
  217. static void                DisposeBuffers(void)
  218.     {
  219.         while (NextAvailableBuffer != NIL)
  220.             {
  221.                 MyStructure*        Temp;
  222.  
  223.                 Temp = NextAvailableBuffer;
  224.                 if (NextAvailableBuffer->Next == NextAvailableBuffer)
  225.                     {
  226.                         /* degenerate */
  227.                         NextAvailableBuffer = NIL;
  228.                     }
  229.                  else
  230.                     {
  231.                         NextAvailableBuffer->Previous->Next = NextAvailableBuffer->Next;
  232.                         NextAvailableBuffer->Next->Previous = NextAvailableBuffer->Previous;
  233.                         NextAvailableBuffer = NextAvailableBuffer->Next;
  234.                     }
  235.                 ReleasePtr(Temp->SampleArea);
  236.                 ReleasePtr((char*)Temp);
  237.             }
  238.     }
  239.  
  240.  
  241. #ifdef THINK_C
  242.     #if __option(profile)
  243.         #define Profiling (True)
  244.     #else
  245.         #define Profiling (False)
  246.     #endif
  247.  
  248.     #pragma options(!profile)
  249. #endif
  250.  
  251. /* asynchronous callback routine which marks buffers as now unused */
  252. /* It would be very bad to profile this since profiling tampers with the */
  253. /* stack invocation and uses A5 global variables!  (believe me, I've tried) */
  254. static pascal void    MyCallBack(SndChannel* Channel, SndCommand* Command)
  255.   {
  256.     *(short*)(Command->param2) = 0;
  257.   }
  258.  
  259. #ifdef THINK_C
  260.     #if Profiling
  261.         #pragma options(profile)
  262.     #endif
  263. #endif
  264.  
  265.  
  266. /* close the sound channel and clean up the buffers */
  267. /* waits until all buffers have played out */
  268. void                CloseSoundChannel(void (*Callback)(void* Refcon), void* Refcon)
  269.     {
  270.         MyStructure*        Scan;
  271.  
  272.         ERROR(!SoundSystemInUse,PRERR(ForceAbort,
  273.             "CloseSoundChannel called, but sound channel isn't open"));
  274.         SoundSystemInUse = False;
  275.      LoopPoint:
  276.         Scan = NextAvailableBuffer;
  277.         do
  278.             {
  279.                 if (Scan->InUseFlag)
  280.                     {
  281.                         if (Callback != NIL)
  282.                             {
  283.                                 (*Callback)(Refcon);
  284.                             }
  285.                         goto LoopPoint;
  286.                     }
  287.                 Scan = Scan->Next;
  288.             } while (Scan != NextAvailableBuffer /* "Full Circle" */);
  289.         /* dispose of the blasted thing */
  290.         SndDisposeChannel(MySoundChannel,True/*shutupnow*/);
  291.         /* release the memory */
  292.         DisposeBuffers();
  293.     }
  294.  
  295.  
  296. /* obtain a pointer to one of the [nonrelocatable] sound buffers.  Data in this */
  297. /* buffer is interpreted as such:  For 8-bit mono, the buffer is an array of */
  298. /* signed chars.  For 16bit mono, the buffer is an array of 2-byte signed integers in */
  299. /* the machine's native endianness.  For 8-bit stereo, the buffer is an array of */
  300. /* 2-byte tuples; the byte lower in memory is the left channel.  For 16-bit stereo, */
  301. /* the buffer is an array of 2-(2-byte) tuples, the left channel is lower in memory. */
  302. /* If there are no buffers currently available (and new ones couldn't be allocated) */
  303. /* then it returns NIL */
  304. char*                CheckOutSoundBuffer(void)
  305.     {
  306.         ERROR(!SoundSystemInUse,PRERR(ForceAbort,
  307.             "CheckOutSoundBuffer called, but sound channel isn't open"));
  308.         ERROR(IsCurrentBufferCheckedOut,PRERR(ForceAbort,
  309.             "CheckOutSoundBuffer called while a buffer has already been checked out"));
  310.         if (NextAvailableBuffer->InUseFlag)
  311.             {
  312.                 /* oops, buffer is in use, try to make another */
  313.                 if (CurrentBufferCount < MaxBuffersToAllocate)
  314.                     {
  315.                         AllocateANewBuffer();
  316.                     }
  317.                 if (NextAvailableBuffer->InUseFlag)
  318.                     {
  319.                         return NIL;
  320.                     }
  321.             }
  322.         IsCurrentBufferCheckedOut = True;
  323.         return NextAvailableBuffer->SampleArea;
  324.     }
  325.  
  326.  
  327. /* submit a buffer to be queued to the system's sound channel.  The number of frames */
  328. /* in the buffer actually used is specified to allow less than the full buffer to */
  329. /* be used. */
  330. void                SubmitBuffer(char* Buffer, long NumUsedFrames,
  331.                             void (*Callback)(void* Refcon), void* Refcon)
  332.     {
  333.         OSErr            Error;
  334.         long            Scan;
  335.  
  336.         ERROR(!SoundSystemInUse,PRERR(ForceAbort,
  337.             "SubmitBuffer called, but sound channel isn't open"));
  338.         ERROR(!IsCurrentBufferCheckedOut,PRERR(ForceAbort,
  339.             "SubmitBuffer called but no buffer has been checked out"));
  340.         ERROR(Buffer != NextAvailableBuffer->SampleArea,
  341.             PRERR(ForceAbort,"SubmitBuffer:  Wrong buffer was submitted"));
  342.         ERROR((NumUsedFrames < 0) || (NumUsedFrames > MaxFramesPerBuffer),
  343.             PRERR(ForceAbort,"SubmitBuffer:  Number of used frames exceeds buffer size!"));
  344.         IsCurrentBufferCheckedOut = False;
  345.         ERROR(NextAvailableBuffer->InUseFlag,PRERR(ForceAbort,
  346.             "SubmitBuffer:  Internal error -- InUseFlag is set but shouldn't be"));
  347.         /* adjust the signed samples to be unsigned */
  348.         /* due to Apple's silliness, this only needs to be done for 8-bit samples */
  349.         if (StereoFlag)
  350.             {
  351.                 if (!SixteenBitFlag)
  352.                     {
  353.                         /* stereo, 8-bit */
  354.                         for (Scan = (2 * NumUsedFrames) - 1; Scan >= 0; Scan -= 1)
  355.                             {
  356.                                 ((char*)Buffer)[Scan] += 0x80;
  357.                             }
  358.                     }
  359.             }
  360.          else
  361.             {
  362.                 if (!SixteenBitFlag)
  363.                     {
  364.                         /* mono, 8-bit */
  365.                         for (Scan = NumUsedFrames - 1; Scan >= 0; Scan -= 1)
  366.                             {
  367.                                 ((char*)Buffer)[Scan] += 0x80;
  368.                             }
  369.                     }
  370.             }
  371.         /* submit the command to actually play the buffer */
  372.      TryAgainPoint1:
  373.         NextAvailableBuffer->InUseFlag = True;
  374.         NextAvailableBuffer->MySoundCommand.cmd = bufferCmd;
  375.         NextAvailableBuffer->MySoundCommand.param1 = 0;
  376.         NextAvailableBuffer->MySoundCommand.param2 = (long)&(NextAvailableBuffer->Header);
  377.         NextAvailableBuffer->Header.numFrames = NumUsedFrames;
  378.         Error = SndDoCommand(MySoundChannel,&(NextAvailableBuffer->MySoundCommand),True);
  379.         if (queueFull == Error)
  380.             {
  381.                 /* oops, we filled up the OS queue; wait a little and try again */
  382.                 if (Callback != NIL)
  383.                     {
  384.                         (*Callback)(Refcon);
  385.                     }
  386.                 goto TryAgainPoint1;
  387.             }
  388.         /* submit the callback routine request.  the callback is an interrupt level */
  389.         /* thing that clears a buffer so we can use it again. */
  390.      TryAgainPoint2:
  391.         NextAvailableBuffer->MyCallbackCommand.cmd = callBackCmd;
  392.         /* say where to store the "0" */
  393.         NextAvailableBuffer->MyCallbackCommand.param2
  394.             = (long)&(NextAvailableBuffer->InUseFlag);
  395.         Error = SndDoCommand(MySoundChannel,&(NextAvailableBuffer->MyCallbackCommand),True);
  396.         if (queueFull == Error)
  397.             {
  398.                 if (Callback != NIL)
  399.                     {
  400.                         (*Callback)(Refcon);
  401.                     }
  402.                 goto TryAgainPoint2;
  403.             }
  404.         /* advance to the next buffer */
  405.         NextAvailableBuffer = NextAvailableBuffer->Next;
  406.     }
  407.  
  408.  
  409. /* discard all queued data on the sound channel and close it immediately */
  410. void                KillSoundChannel(void)
  411.     {
  412.         ERROR(!SoundSystemInUse,PRERR(ForceAbort,
  413.             "KillSoundChannel:  but sound channel isn't open"));
  414.  
  415.         /* dispose of the sound channel */
  416.         SndDisposeChannel(MySoundChannel,True/*shutupnow*/);
  417.  
  418.         /* release buffers */
  419.         DisposeBuffers();
  420.  
  421.         /* mark sound subsystem as available */
  422.         SoundSystemInUse = False;
  423.     }
  424.